/*
 * Copyright (c) www.bugull.com
 */

package com.bugull.farm.app.server;

import com.bugull.farm.app.server.factory.SslContextFactory;
import com.bugull.farm.app.server.handler.AppChannelStatusHandler;
import com.bugull.farm.app.server.handler.MyAppFrameHandler;
import com.bugull.farm.app.server.listener.AppTopicListener;
import com.bugull.farm.core.cache.PropertiesCache;
import com.bugull.farm.core.codec.DefaultFrameDecoder;
import com.bugull.farm.core.frame.loader.PropertiesLoader;
import com.bugull.farm.core.frame.loader.XmlLoader;
import com.bugull.farm.core.handler.DefaultValidateHandler;
import com.bugull.farm.core.handler.FarmIdleChannelHandler;
import com.bugull.farm.core.server.FarmServer;
import com.bugull.farm.core.server.ZKClient;
import com.bugull.farm.core.utils.StringUtil;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

/**
 *
 * @author Frank Wen(xbwen@hotmail.com)
 */
public class AppServer extends FarmServer {
    
	private SSLContext sslCtx;
    public static String ip;
    public final static AtomicLong channelCount = new AtomicLong();
    
    private void init() throws Exception{
    	//init redis
        listenRedisTopic(PropertiesCache.getInstance().get("redis.topic.app"), new AppTopicListener());

        //init mongodb
        connectMongodb();
        
        //add shutdown hook
        this.addShutdownHook();
    }
    
    private void start() throws Exception {
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup();
        ServerBootstrap b = new ServerBootstrap();
        sslCtx = SslContextFactory.getServerContext();
        ChannelInitializer<SocketChannel> channelInit = new ChannelInitializer<SocketChannel>(){
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                int idleTime = (int)(1.5F * PropertiesCache.getInstance().getInt("app.heartbeat.interval"));
                SSLEngine sslEngine = sslCtx.createSSLEngine();
         		sslEngine.setUseClientMode(false);
                ch.pipeline()
                        .addFirst(new AppChannelStatusHandler())
                        .addLast(new IdleStateHandler(idleTime, 0, 0, TimeUnit.SECONDS))
                        .addLast(new FarmIdleChannelHandler())
                        .addLast(new SslHandler(sslEngine))
                        .addLast(new LengthFieldBasedFrameDecoder(1024, 8, 2))
                        .addLast(new DefaultFrameDecoder())
                        .addLast(new DefaultValidateHandler())
                        .addLast(new MyAppFrameHandler());
            }
        };
        b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class)
            .option(ChannelOption.SO_BACKLOG, 1024)
            .childHandler(channelInit)
            .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE);
        try{
            ChannelFuture f = b.bind(PropertiesCache.getInstance().getInt("app.server.port")).sync();
            System.out.println("AppServer start listen on port " + PropertiesCache.getInstance().getInt("app.server.port") + "......");
            f.channel().closeFuture().sync();
        }finally{
            this.doShutdown();
        }
    }
    
    public static void main(String[] args) throws Exception {
    	//加载配置文件
    	String path = System.getProperty("user.dir");
    	path = path.substring(0, path.lastIndexOf(File.separator) + 1);
    	String xmlPath = path + File.separator + "xml";
    	String propsPath = path + "properties.properties";
    	//加载通信协议模板文件
        XmlLoader xLoader = new XmlLoader();
        xLoader.load(xmlPath);
        //加载配置文件
        PropertiesLoader pLoader = new PropertiesLoader();
        pLoader.load(propsPath);
        
        ZKClient zkClient = ZKClient.getInstance();
        zkClient.connect(PropertiesCache.getInstance().get("zk.conn"));
        zkClient.setIp(PropertiesCache.getInstance().get("zk.app.workers"), PropertiesCache.getInstance().get("dev.local.ip"));
        
        AppServer server = new AppServer();
        server.init();
        server.start();
    }

}
